package cn.xinfei.xdecision.data.core.frame.executors.groovy.check.impl;


import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import cn.xinfei.xdecision.data.core.frame.executors.groovy.check.ExpressionService;
import cn.xinfei.xdecision.data.core.util.ExcelUtil;
import cn.xinfei.xdecision.data.core.util.ExpressionUtil;
import cn.xinfei.xdecision.data.core.vo.groovy.MixExpressionCheck;
import cn.xinfei.xdecision.data.core.vo.groovy.MixExpressionCheckResult;
import cn.xinfei.xdecision.data.core.vo.groovy.MixExpressionTestData;
import cn.xinfei.xdecision.data.core.vo.groovy.VarTestData;
import cn.xinfei.xdecision.data.core.vo.groovy.rule.RuleMixExpressionCheck;
import cn.xinfei.xdecision.data.core.vo.groovy.rule.RuleMixExpressionCheckResult;
import com.monitorjbl.xlsx.StreamingReader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

@Service
@Slf4j
public class ExpressionServiceImpl implements ExpressionService {

    @Autowired
    private ExpressionUtil expressionUtil;

    private static final Integer MAX_ROW_COUNT = 5000;

    private static final Integer MAX_READ_ROW_COUNT = 40000;

    private static final String CHECK_RESULT = "校验结果";

    private static final String RESULT_MESSAGE = "结果描述";

    private static final String HOPE_RESULT = "期望结果";

    @Override
    public List<MixExpressionCheckResult> check(MixExpressionCheck mixExpressionCheck) {
        return expressionUtil.testExpression(mixExpressionCheck);
    }

//    @Override
//    public List<RuleMixExpressionCheckResult> scriptRulecheck(RuleMixExpressionCheck mixExpressionCheck) {
//        return expressionUtil.testScriptRule(mixExpressionCheck);
//    }

    @Override
    public void getTemplate(HttpServletResponse response, String id, String expression, String expressionResultType, List<String> varCodes) throws Exception {
        String fileName = "表达式校验模版_" + id;
        ExcelUtil.exportExcelWithStyle(response, fileName, "sheet1", getRows(expression, expressionResultType, varCodes));
    }

    private List<List<String>> getRows(String expression, String expressionResultType, List<String> elementIds) {
        List<String> row1 = new ArrayList<>();
        row1.add("表达式校验");
        row1.add("限制4W行数据");
        List<String> row2 = new ArrayList<>();
        row2.add("表达式");
        row2.add("表达式结果类型");
        List<String> row3 = new ArrayList<>();
        row3.add(expression);
        row3.add(expressionResultType);
        List<String> row4 = new ArrayList<>();
        row4.add("测试数据");
        List<String> row5 = new ArrayList<>(elementIds);
        row5.add("期望结果");
        row5.add("校验结果");
        row5.add("结果描述");

        List<List<String>> keysForSheets = new ArrayList<>();
        keysForSheets.add(row1);
        keysForSheets.add(row2);
        keysForSheets.add(row3);
        keysForSheets.add(row4);
        keysForSheets.add(row5);
        return keysForSheets;
    }

    @Override
    public void batchCheck(HttpServletResponse response, String id, MultipartFile file) throws Exception {
        Workbook wb;

        String fileName = file.getOriginalFilename();
        String ext = fileName.substring(fileName.lastIndexOf("."));
        Map<Integer, List<String>> map = new HashMap<>();
        try (InputStream is = file.getInputStream()) {
            if (".xlsx".equals(ext)) {
                wb = StreamingReader.builder()
                        .rowCacheSize(MAX_READ_ROW_COUNT)
                        .bufferSize(10 * 1024 * 1024)
                        .open(is);

            } else {
                log.error("不支持的文件格式：{}", ext);
                throw new XDecisionDataException(
                        XDecisionDataExceptionType.FILE_UPLOAD_ERROR, "文件格式异常"
                );
            }
        } catch (IOException e) {
            log.error("read excel error:{}", e.getCause(), e);
            throw new XDecisionDataException(
                    XDecisionDataExceptionType.FILE_UPLOAD_ERROR, "excel 文件读取异常", e
            );
        }
        Sheet sheet = wb.getSheetAt(0);
        for (Row row : sheet) {
            Integer index = row.getRowNum();
            if (index >= MAX_READ_ROW_COUNT) {
                break;
            }
            List<String> items = new ArrayList<>();
            //遍历所有的列
            for (Cell cell : row) {
                items.add(cell.getStringCellValue());
            }
            map.put(index, items);
        }
        int maxSize = map.size();
        if (maxSize < 5) {
            log.error("文件无有效数据，rows:{}", maxSize);
            throw new XDecisionDataException(
                    XDecisionDataExceptionType.FILE_UPLOAD_ERROR, "文件无有效数据"
            );
        }
        String expression = StringUtil.replaceSpecialStr(map.get(2).get(0));
        String expressionResultType = map.get(2).get(1);
        log.info("expression:{},expressionResultType:{}", expression, expressionResultType);

        List<String> headRows = map.get(4);
        short checkResultNum = -1;
        short checkMessageNum = -1;
        Integer hopeResultNum = -1;
        for (int i = 0; i < headRows.size(); i++) {
            String result = headRows.get(i);

            if (HOPE_RESULT.equals(result)) {
                hopeResultNum = i;
                continue;
            }

            if (CHECK_RESULT.equals(result)) {
                checkResultNum = (short) i;
                continue;
            }

            if (RESULT_MESSAGE.equals(result)) {
                checkMessageNum = (short) i;
            }

        }

        List<MixExpressionCheckResult> checkResults = new ArrayList<>();
        //分批读取.从第五行开始读
        log.info("****** 文件读取开始 ******");
        int totalBatchNo = (maxSize - 5) % MAX_ROW_COUNT == 0 ? (maxSize - 5) / MAX_ROW_COUNT : (maxSize - 5) / MAX_ROW_COUNT + 1;
        for (int batchNo = 0; batchNo < totalBatchNo; batchNo++) {
            int end = Math.min((batchNo + 1) * MAX_ROW_COUNT + 5, maxSize);
            log.info("第{}批读取开始，共{}批.", batchNo, totalBatchNo);
            List<MixExpressionTestData> testDataList = new ArrayList<>();
            for (int rowIndex = batchNo * MAX_ROW_COUNT + 5; rowIndex < end; rowIndex++) {
                List<String> rows = map.get(rowIndex);
                if (CollectionUtils.isEmpty(rows) || StringUtils.isEmpty(rows.get(0))) {
                    continue;
                }

                List<VarTestData> dataList = fillParam(headRows.size(), headRows, rows);

                MixExpressionTestData testData = new MixExpressionTestData();
                testData.setRowId(rowIndex);
                testData.setDesiredResult(StringUtil.replaceSpecialStr(rows.get(hopeResultNum > -1 ? hopeResultNum : rows.size() - 1)));
                testData.setTestDatas(dataList);
                testDataList.add(testData);

            }
            if (CollectionUtils.isNotEmpty(testDataList)) {
                MixExpressionCheck mixExpressionCheck = new MixExpressionCheck();
                mixExpressionCheck.setId(id);
                mixExpressionCheck.setExpression(expression);
                mixExpressionCheck.setExpressionResultType(expressionResultType);
                mixExpressionCheck.setMixExpressionTestDataList(testDataList);
                checkResults.addAll(check(mixExpressionCheck));
            }
        }
        log.info("****** 文件读取结束 ******");
        log.info("结果文件开始写出");
        ExcelUtil.exportExcelWithStyle(response, "expression_check_result_" + id, "Sheet1", result2List(map, checkResults, checkResultNum, checkMessageNum));

    }


    private List<List<String>> result2List(Map<Integer, List<String>> map, List<MixExpressionCheckResult> checkResults, short checkResultIndex, short checkMessageIndex) {
        List<List<String>> newRows = new ArrayList<>();
        map.forEach((k, v) -> {
            if (k < 5) {
                newRows.add(v);
                return;
            }
            Optional<MixExpressionCheckResult> checkResult = checkResults.stream().filter(x -> x.getRowId().equals(k)).findFirst();
            MixExpressionCheckResult mixExpressionCheckResult = checkResult.orElse(null);
            //如果没有检测结果，不导出
            if (Objects.isNull(mixExpressionCheckResult)) {
                return;
            }
            if (checkResultIndex > -1) {
                addIndex(checkResultIndex, v, mixExpressionCheckResult.getTestResult());
            } else {
                v.add(mixExpressionCheckResult.getTestResult());
            }
            if (checkMessageIndex > -1) {
                addIndex(checkMessageIndex, v, mixExpressionCheckResult.getMessage());
            } else {
                v.add(mixExpressionCheckResult.getMessage());
            }
            newRows.add(v);
        });
        return newRows;
    }

    /**
     * 组装接口请求参数
     */
    private List<VarTestData> fillParam(int colSize, List<String> headRows, List<String> rows) {
        List<VarTestData> testDatas = new ArrayList<>();
        try {
            for (int index = 0; index < colSize - 3; index++) {//减去最后三列：期望结果、校验结果、结果描述
                VarTestData testData = new VarTestData();
                String paramKey = headRows.get(index);
                String paramValue = rows.get(index);

                testData.setVarCode(paramKey.substring(0, paramKey.indexOf("/")));
                testData.setVarValue(paramValue);
                testData.setValueType(paramKey.substring(paramKey.lastIndexOf("/") + 1));
                testDatas.add(testData);
            }
            return testDatas;
        } catch (Exception e) {
            log.error("测试数据读取异常！e:" + e);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 对齐excel导出格式
     */
    private void addIndex(int index, List<String> list, String element) {
        if (index < list.size()) {
            list.set(index, element);
            return;
        }
        list.add("");
        addIndex(index, list, element);
    }
}
